﻿using System;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using Api.Common;
using AppManager.Model;

namespace AppManager.DAL
{
    public class AppManagerDb : DbContext
    {
        //delegate void EventRegistModel(DbModelBuilder mb);
        //event EventRegistModel register;

        #region ctor
        public AppManagerDb() : base("Db")
        {
            //register += ModelRegister.Regist;
        }

        static AppManagerDb()
        {
            DbConfiguration.SetConfiguration(new MySql.Data.Entity.MySqlEFConfiguration());
            Database.SetInitializer<AppManagerDb>(null);
            //总是删除            
            //Database.SetInitializer(new DropCreateDatabaseAlways<Db>());
            //自动迁移,發佈到線上不應該使用這個配置
            //Database.SetInitializer(new MigrateDatabaseToLatestVersion<Db, Configuration>());

            //if (AppDomainHelper.Instance.GetStatus() == ServerStatus.publish)
            //{
            //    Database.SetInitializer<Db>(new CreateDatabaseIfNotExists<Db>());
            //}
            //else
            //{
            //    //自动迁移,發佈到線上不應該使用這個配置
            //    Database.SetInitializer(new MigrateDatabaseToLatestVersion<Db, Configuration>());
            //}
        }
        #endregion

        #region Tables
        public DbSet<Sys_User> Users { get; set; }
        public DbSet<Sys_Menu> Menus { get; set; }
        public DbSet<Sys_Role> Roles { get; set; }
        public DbSet<App> Apps { get; set; }
        public DbSet<AppInfo> AppInfos { get; set; }
        #endregion

        #region Instance

        /// <summary>
        /// 返回當前使用的Db實例
        /// </summary>
        public static AppManagerDb Current
        {
            get
            {
                var m = CallContext.GetData("apidb") as AppManagerDb;
                if (m == null)
                {
                    m = new AppManagerDb();
                    CallContext.SetData("apidb", m);
                }
                return m;
            }
        }

        /// <summary>
        /// 返回一個全新的Db 實例
        /// </summary>
        public static AppManagerDb NewInstance
        {
            get
            {
                return new AppManagerDb();
            }
        }
        #endregion

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            //移除表复数名称
            modelBuilder.Conventions
                .Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention>();

            base.OnModelCreating(modelBuilder);
        }


        /// <summary>
        /// 局部更新（好像没用了吧）
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="propertyExpression"></param>
        /// <param name="entities"></param>
        /// <returns></returns>
        public int Update<TEntity>(Expression<Func<TEntity, object>> propertyExpression, params TEntity[] entities)
          where TEntity : BaseModel
        {
            if (propertyExpression == null) throw new ArgumentNullException("propertyExpression");
            if (entities == null) throw new ArgumentNullException("entities");
            ReadOnlyCollection<MemberInfo> memberInfos = ((dynamic)propertyExpression.Body).Members;
            foreach (TEntity entity in entities)
            {
                try
                {
                    DbEntityEntry<TEntity> entry = Current.Entry(entity);
                    entry.State = EntityState.Unchanged;//這個應該需要，因為這個修改了property的isModify后，該屬性就變成了modify，但是設置了好像又不能更新
                    foreach (var memberInfo in memberInfos)
                    {
                        entry.Property(memberInfo.Name).IsModified = true;
                    }
                }
                catch (InvalidOperationException)
                {
                    TEntity originalEntity = Current.Set<TEntity>().Local.Single(m => m.Id == entity.Id);
                    ObjectContext objectContext = ((IObjectContextAdapter)Current).ObjectContext;
                    ObjectStateEntry objectEntry = objectContext.ObjectStateManager.GetObjectStateEntry(originalEntity);
                    objectEntry.ApplyCurrentValues(entity);
                    objectEntry.ChangeState(EntityState.Unchanged);
                    foreach (var memberInfo in memberInfos)
                    {
                        objectEntry.SetModifiedProperty(memberInfo.Name);
                    }
                }
            }
            return AppManagerDb.Current.SaveChanges();
        }

        /// <summary>
        /// 局部更新
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="IgnorepropertyExpression"></param>
        /// <param name="entities"></param>
        /// <returns></returns>
        public int UpdateIgnore<TEntity>(Expression<Func<TEntity, object>> IgnorepropertyExpression, params TEntity[] entities)
          where TEntity : BaseModel
        {
            if (IgnorepropertyExpression == null) throw new ArgumentNullException("propertyExpression");
            if (entities == null) throw new ArgumentNullException("entities");
            ReadOnlyCollection<MemberInfo> ingoreMemberInfos = ((dynamic)IgnorepropertyExpression.Body).Members;
            var ignoreNames = ingoreMemberInfos.Select(s => s.Name);
            var properties = typeof(TEntity).GetProperties().Where(s => !ignoreNames.Contains(s.Name));

            foreach (TEntity entity in entities)
            {
                try
                {
                    DbEntityEntry<TEntity> entry = Current.Entry(entity);
                    entry.State = EntityState.Unchanged;
                    foreach (var memberInfo in properties)
                    {
                        try
                        {
                            entry.Property(memberInfo.Name).IsModified = true;
                        }
                        catch
                        {
                            //某些List集合的屬性設置該屬性時會發生異常
                        }
                    }
                }
                catch (InvalidOperationException)
                {
                    TEntity originalEntity = Current.Set<TEntity>().Local.Single(m => m.Id == entity.Id);
                    ObjectContext objectContext = ((IObjectContextAdapter)Current).ObjectContext;
                    ObjectStateEntry objectEntry = objectContext.ObjectStateManager.GetObjectStateEntry(originalEntity);
                    objectEntry.ApplyCurrentValues(entity);
                    objectEntry.ChangeState(EntityState.Unchanged);
                    foreach (var memberInfo in properties)
                    {
                        try
                        {
                            objectEntry.SetModifiedProperty(memberInfo.Name);
                        }
                        catch
                        {
                            //某些List集合的屬性設置該屬性時會發生異常
                        }
                    }
                }
            }
            return AppManagerDb.Current.SaveChanges();
        }
    }
}
